﻿using Basic.Cluster.Abstract;
using Basic.Cluster.Manager;
using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Security.Cryptography;

namespace Basic.Cluster.Impl;

public sealed class ConsulRegister : IRegister, IDisposable
{
    private ConsulClient _client;
    private ClusterConfig _config;
    private IServiceProvider _serviceProvider;

    /// <summary>
    /// 添加服务注册
    /// </summary>
    /// <param name="services"></param>
    /// <param name="config"></param>
    /// <returns></returns>
    public IServiceCollection AddRegister(IServiceCollection services, ClusterConfig config)
    {
        _config = config;
        _client = new ConsulClient(s =>
        {
            s.Address = new Uri(config.ClusterManagerAddress);
            s.Token = config.SecretID;
        });
        var confType = typeof(IConfiguration);
        var configuration = services.FirstOrDefault(d => d.ServiceType == confType)?.ImplementationInstance as IConfiguration;
        RefreshKeyValve(configuration).GetAwaiter().GetResult();

        services.AddSingleton<ClusterConfig>(_config);
        services.AddSingleton<ConsulClient>(_client);
        services.AddHealthChecks();

        var discover = new ConsulDiscover();
        discover.AddRegister(_client);
        services.AddSingleton<IDiscover>(discover);

        StartTimer();
        return services;
    }

    /// <summary>
    /// 启动服务注册
    /// </summary>
    /// <param name="app"></param>
    /// <returns></returns>
    public IApplicationBuilder UseRegister(IApplicationBuilder app)
    {
        var consulClient = _client;
        var confInfo = _config;
        app.UseHealthChecks("/health");

        var registration = CreateRegistration(confInfo);
        // 获取主机生命周期对象
        var lifetime = app.ApplicationServices.GetRequiredService<IHostApplicationLifetime>();
        lifetime.ApplicationStarted.Register(async () =>
        {
            _ = await consulClient.Agent.ServiceRegister(registration);
        });
        lifetime.ApplicationStopping.Register(async () =>
        {
            _ = await consulClient.Agent.ServiceDeregister(registration.ID);
        });

        return app;
    }

    /// <summary>
    /// 创建http服务注册信息对象
    /// </summary>
    /// <param name="confInfo"></param>
    /// <returns></returns>
    private AgentServiceRegistration CreateRegistration(ClusterConfig confInfo)
    {
        AgentServiceCheck check = new AgentServiceCheck
        {
            CheckID = confInfo.ID,
            HTTP = confInfo.HealthCheck,
            Interval = TimeSpan.FromSeconds(confInfo.HealthInterval ?? 30),
            Timeout = TimeSpan.FromSeconds(confInfo.HealthInterval ?? 5),
            DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(2),
        };
        return new AgentServiceRegistration()
        {
            Check = check,
            ID = confInfo.ID,
            Name = confInfo.Name,
            Address = confInfo.IP,
            Port = confInfo.Port,
            Meta = confInfo.ServiceMeta,
        };
    }

    #region 刷新配置

    private System.Timers.Timer _timer;
    private Dictionary<string, string> KeyHashDict = new Dictionary<string, string>();

    private void StartTimer()
    {
        _timer = new System.Timers.Timer();
        _timer.Interval = (_config.ConfigCheckInterval < 10 ? 10 : _config.ConfigCheckInterval) * 1000d;
        _timer.Elapsed += async (sender, args) => await RefreshKeyValve(null);
        _timer.Start();
    }

    /// <summary>
    /// 刷新consul KeyValve
    /// </summary>
    /// <returns></returns>
    private async Task RefreshKeyValve(IConfiguration configuration)
    {
        configuration ??= _serviceProvider?.GetService<IConfiguration>();
        if (_config.Keys == null || _config.Keys.Length == 0 || configuration == null)
            return;

        try
        {
            foreach (string key in _config.Keys)
            {
                QueryResult<KVPair> result = await _client.KV.Get(key);
                if (result.StatusCode == System.Net.HttpStatusCode.OK && result.Response.Value?.Length > 0)
                {
                    byte[] jsonBytes = result.Response.Value;
                    string currentHash = CalcHash(jsonBytes);
                    if (!(KeyHashDict.TryGetValue(key, out string oldHash) && string.Equals(currentHash, oldHash)))
                    {
                        using var stream = new MemoryStream(jsonBytes);
                        if (configuration is ConfigurationManager manager)
                            manager.AddJsonStream(stream);
                    }
                    KeyHashDict[key] = currentHash;
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"ConsulRegister.RefreshKeyValve() Error:{ex.Message}");
        }
    }

    #endregion

    private static string CalcHash(byte[] raw)
    {
        try
        {
            using var md5 = MD5.Create();
            return Convert.ToBase64String(md5.ComputeHash(raw));
        }
        catch { }
        return null;
    }

    #region Dispose

    private bool disposedValue;
    private void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                // TODO: 释放托管状态(托管对象)
                _client?.Dispose();
                _timer?.Stop();
                _timer?.Dispose();
            }
            // TODO: 释放未托管的资源(未托管的对象)并重写终结器
            // TODO: 将大型字段设置为 null
            _client = null;
            _timer = null;
            disposedValue = true;
        }
    }

    public void Dispose()
    {
        // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
        Dispose(disposing: true);
        GC.SuppressFinalize(this);
    }

    #endregion

}